package migu

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/sirupsen/logrus"
	"micro-grpc-server/common"
	"micro-grpc-server/library/cache"
	music "micro-grpc-server/services/music/proto"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

const playUrl = "http://app.pd.nf.migu.cn/MIGUM2.0/v1.0/content/sub/listenSong.do?toneFlag=%s&netType=00&userId=15548614588710179085069&ua=Android_migu&version=5.1&copyrightId=0&contentId=%s&resourceType=%s&channel=0"

type Music struct {
	Id             string          `json:"id"`
	ContentId      string          `json:"contentId"`
	CopyrightId    string          `json:"copyrightId"`
	Name           string          `json:"name"`
	Singers        []Singer        `json:"singers"`
	Albums         []Album         `json:"albums"`
	Tags           []string        `json:"tags"`
	LyricUrl       string          `json:"lyricUrl"`
	ImgItems       []ImgItem       `json:"imgItems"`
	NewRateFormats []NewRateFormat `json:"newRateFormats"`
	InvalidateDate string          `json:"invalidateDate"`
}

type songResultData struct {
	TotalCount string   `json:"totalCount"`
	Result     []*Music `json:"result"`
}

type SearchResponse struct {
	Code           string          `json:"code"`
	Info           string          `json:"info"`
	SongResultData *songResultData `json:"songResultData"`
}

type Singer struct {
	Id   string `json:"id"`
	Name string `json:"name"`
}

func (s Singer) ToProto() *music.Singer {
	return &music.Singer{
		Id:   s.Id,
		Name: s.Name,
	}
}

type Album struct {
	Id   string `json:"id"`
	Name string `json:"name"`
}

func (s Album) ToProto() *music.Album {
	return &music.Album{
		Id:   s.Id,
		Name: s.Name,
	}
}

type ImgItem struct {
	ImgSizeType string `json:"imgSizeType"`
	Img         string `json:"img"`
}

func (s ImgItem) ToProto() *music.ImgItem {
	return &music.ImgItem{
		ImgSizeType: s.ImgSizeType,
		Img:         s.Img,
	}
}

type NewRateFormat struct {
	ResourceType string `json:"resourceType"`
	FormatType   string `json:"formatType"`
	IosUrl       string `json:"iosUrl"`
	AndroidUrl   string `json:"androidUrl"`
	Url          string `json:"url"`
}

func (s NewRateFormat) ToProto() *music.NewRateFormat {
	return &music.NewRateFormat{
		ResourceType: s.ResourceType,
		FormatType:   s.FormatType,
		IosUrl:       s.IosUrl,
		AndroidUrl:   s.AndroidUrl,
		Url:          s.Url,
	}
}

type lyricChan struct {
	CopyrightId string
}

type Server struct {
	SearchUrl         string
	LyricUrl          *url.URL
	Client            *http.Client
	cache             cache.Cache
	lyricCacheKey     string
	lyricChanMessages chan *lyricChan
}

func NewServer(cache cache.Cache) *Server {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}
	u, err := url.Parse("https://music.migu.cn/v3/api/music/audioPlayer/getLyric")
	if err != nil {
		return nil
	}
	client := &http.Client{Transport: tr}
	server := &Server{
		SearchUrl:         "https://pd.musicapp.migu.cn/MIGUM3.0/v1.0/content/search_all.do",
		LyricUrl:          u,
		Client:            client,
		cache:             cache,
		lyricCacheKey:     "lyric_%s_cache",
		lyricChanMessages: make(chan *lyricChan, 100),
	}
	go server.consumeLyricMsg()
	return server
}

// 关闭通道
func (s *Server) Close() {
	close(s.lyricChanMessages)
}

// 将通道中的歌词id拿去换取真正的歌词
func (s *Server) consumeLyricMsg() {
	fmt.Println("进入到消费的地方了")
	for msg := range s.lyricChanMessages {
		if s.checkCacheKeyExist(msg.CopyrightId) {
			continue
		}
		go s.cacheLyric(msg.CopyrightId)
	}
	fmt.Println("结束消费")
}

// 搜索歌曲
func (s *Server) Search(rq *music.ListMusicRequest) (list []*music.Music, total int64, err error) {
	var params = url.Values{}
	params.Add("ua", "Android_migu")
	params.Add("version", "5.0.1")
	params.Add("searchSwitch", "{\"song\":1,\"album\":0,\"singer\":0,\"tagSong\":0,\"mvSong\":0,\"songlist\":0,\"bestShow\":0}")
	params.Add("text", rq.Text)
	params.Add("pageNo", strconv.FormatInt(rq.Page, 10))
	params.Add("pageSize", strconv.FormatInt(rq.Limit, 10))

	var res *http.Response
	res, err = s.Client.Get(fmt.Sprintf("%s?data=%s", s.SearchUrl, params.Encode()))
	if err != nil {
		return nil, 0, err
	}
	defer res.Body.Close()
	var result = &SearchResponse{}
	if err := common.LoadResponse(res, result); err != nil {
		return nil, 0, err
	}
	if result.Code != "000000" {
		return nil, 0, errors.New(result.Info)
	}

	total, err = strconv.ParseInt(result.SongResultData.TotalCount, 10, 64)
	if err != nil {
		return nil, 0, errors.New("总数页码转换失败")
	}

	for _, item := range result.SongResultData.Result {
		list = append(list, s.musicToProto(item))
	}

	return list, total, nil
}

// 将接口返回的数据转换为proto规定的格式
func (s *Server) musicToProto(m *Music) *music.Music {
	mu := &music.Music{
		SongId:         m.Id,
		ContentId:      m.ContentId,
		CopyrightId:    m.CopyrightId,
		Name:           m.Name,
		Tags:           m.Tags,
		LyricUrl:       m.LyricUrl,
		InvalidateDate: common.DateToTimestamp(m.InvalidateDate),
		Singers:        []*music.Singer{},
		Albums:         []*music.Album{},
		ImgItems:       []*music.ImgItem{},
		NewRateFormats: []*music.NewRateFormat{},
	}
	if len(m.Singers) > 0 {
		for _, singer := range m.Singers {
			mu.Singers = append(mu.Singers, singer.ToProto())
		}
	}
	if len(m.Albums) > 0 {
		for _, album := range m.Albums {
			mu.Albums = append(mu.Albums, album.ToProto())
		}
	}
	if len(m.ImgItems) > 0 {
		for _, imgItem := range m.ImgItems {
			mu.ImgItems = append(mu.ImgItems, imgItem.ToProto())
		}
	}
	if len(m.NewRateFormats) > 0 {
		for _, newRateFormat := range m.NewRateFormats {
			mu.NewRateFormats = append(mu.NewRateFormats, newRateFormat.ToProto())
		}
	}
	return mu
}

// 获取播放信息
func (s *Server) GetPlayInfo(rq *music.PLayInfoRequest) (string, string, error) {
	return fmt.Sprintf(playUrl, rq.FormatType, rq.ContentId, rq.ResourceType), s.getLyricOnlyCache(rq.CopyrightId), nil
}

// 获取歌词
func (s *Server) GetLyric(rq *music.LyricRequest) (string, bool) {
	lyric := s.getCacheLyric(rq.CopyrightId)
	if len(lyric) == 0 {
		return "", false
	}
	return lyric, true
}

// 仅仅从缓存读取
func (s *Server) getLyricOnlyCache(copyrightId string) (str string) {
	cacheKey := fmt.Sprintf(s.lyricCacheKey, copyrightId)
	if s.cache.IsExist(cacheKey) {
		lyric := s.cache.Get(cacheKey)
		str, _ = lyric.(string)
	} else {
		s.lyricChanMessages <- &lyricChan{CopyrightId: copyrightId}
	}
	return
}

// 通过缓存获取歌词
func (s *Server) getCacheLyric(copyrightId string) (str string) {
	cacheKey := fmt.Sprintf(s.lyricCacheKey, copyrightId)
	if !s.checkCacheKeyExist(cacheKey) {
		str, _ = s.cacheLyric(copyrightId)
	} else {
		lyric := s.cache.Get(cacheKey)
		str, _ = lyric.(string)
	}
	return
}

// 验证键是否存在
func (s *Server) checkCacheKeyExist(key string) bool {
	return s.cache.IsExist(key)
}

// 缓存歌词
func (s *Server) cacheLyric(copyrightId string) (string, bool) {
	cacheKey := fmt.Sprintf(s.lyricCacheKey, copyrightId)
	logrus.Debug(fmt.Sprintf("开始缓存%s的歌词", cacheKey))
	lyric := s.fetchLyricWithRequest(copyrightId)
	err := s.cache.Set(cacheKey, lyric, 7*24*time.Hour)
	return lyric, err == nil
}

// 歌词接口返回结构
type LyricResponse struct {
	Lyric           string `json:"lyric"`
	Msg             string `json:"msg"`
	ReturnCode      string `json:"returnCode"`
	Sbslyric        string `json:"sbslyric"`
	TranslatedLyric string `json:"translatedLyric"`
}

// 通过http请求获取歌词
func (s *Server) fetchLyricWithRequest(copyrightId string) string {
	s.LyricUrl.RawQuery = fmt.Sprintf("copyrightId=%s", copyrightId)
	var req = &http.Request{
		Method: "GET",
		URL:    s.LyricUrl,
		Host:   s.LyricUrl.Host,
		Header: map[string][]string{
			"referer":    {"https://music.migu.cn/v3/music/player/audio"},
			"user-agent": {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"},
		},
	}
	res, err := s.Client.Do(req)
	if err != nil {
		return ""
	}
	defer res.Body.Close()
	var result = &LyricResponse{}
	if err := common.LoadResponse(res, result); err != nil {
		return ""
	}
	if result.ReturnCode != "000000" {
		return ""
	}
	return result.Lyric
}
